/**
 * ============================================
 * 应用程序主入口文件
 *
 * 功能说明：
 * - Express服务器的启动文件，负责初始化所有中间件和路由
 * - 配置CORS跨域、JSON解析、静态文件服务等核心功能
 * - 提供健康检查、错误处理、优雅关闭等生产级功能
 * - 自动检测可用端口并启动服务器
 *
 * 使用说明：
 * - 直接运行：node app.js
 * - 作为模块导入：const { app, startServer } = require('./app')
 * ============================================
 */

/**
 * ====================
 * 引入依赖模块
 * ====================
 */
// Express框架 - Node.js最流行的Web应用框架
const express = require('express');

// CORS中间件 - 处理跨域资源共享（Cross-Origin Resource Sharing）
const cors = require('cors');

// dotenv库 - 从.env文件加载环境变量
const dotenv = require('dotenv');

// net模块 - Node.js内置网络模块，用于端口检测
const net = require('net');

/**
 * ====================
 * 加载环境变量
 * ====================
 * 从项目根目录的.env文件加载环境变量
 * 环境变量包含数据库连接信息、服务器端口等敏感配置
 */
dotenv.config();

/**
 * ====================
 * 引入数据库和路由模块
 * ====================
 */
// 数据库连接测试函数 - 用于启动时验证数据库连接
const { testConnection } = require('./config/database');

// 路由模块 - 按功能模块划分的路由处理器
const authRoutes = require('./routes/auth');        // 认证相关路由（登录、注册等）
const appRoutes = require('./routes/app');          // 应用通用路由
const fundRoutes = require('./routes/fund');        // 资金管理路由（充值、提现等）
const cardRoutes = require('./routes/card');        // 卡片管理路由（开卡、查询等）
const dashboardRoutes = require('./routes/dashboard'); // 仪表板数据路由

/**
 * ====================
 * 引入中间件
 * ====================
 */
// 错误处理中间件 - 统一处理应用中的错误
const { errorHandler } = require('./middlewares/errorHandler');

/**
 * ====================
 * 创建Express应用实例
 * ====================
 * app对象是Express应用的根对象，用于配置中间件和路由
 */
const app = express();

/**
 * ====================
 * 服务器端口配置
 * ====================
 * 优先使用环境变量中的PORT，如果没有则使用3000作为默认端口
 */
const PORT = process.env.PORT || 3000;

/**
 * ====================
 * 应用中间件配置
 * ====================
 * 中间件按照顺序执行，每个中间件都有特定的功能
 */

/**
 * CORS中间件配置
 * 功能：处理跨域资源共享，允许指定来源的请求
 * 安全考虑：只允许特定的前端域名访问API
 */
const allowedOrigins = [
  'http://localhost:5173',  // Vue开发服务器端口
  'http://localhost:9000'   // 可能的其他前端服务端口
];

app.use(cors({
  origin: function (origin, callback) {
    // 允许没有origin的请求（比如服务器端请求、Postman等工具）
    if (!origin) return callback(null, true);
    
    // 检查请求来源是否在允许列表中
    if (allowedOrigins.indexOf(origin) === -1) {
      const msg = `CORS策略阻止了来自 ${origin} 的请求。允许的来源: ${allowedOrigins.join(', ')}`;
      return callback(new Error(msg), false);
    }
    
    // 允许请求
    return callback(null, true);
  },
  credentials: true  // 允许发送cookie等认证信息
}));

/**
 * JSON解析中间件
 * 功能：自动解析请求体中的JSON数据，转换为JavaScript对象
 * 使用场景：处理前端发送的JSON格式数据
 */
app.use(express.json());

/**
 * URL编码解析中间件
 * 功能：解析application/x-www-form-urlencoded格式的表单数据
 * extended: true 允许解析嵌套对象
 */
app.use(express.urlencoded({ extended: true }));

/**
 * ====================
 * 静态文件服务配置
 * ====================
 * 功能：提供静态文件访问服务，如用户上传的图片等
 * 访问路径：http://localhost:3000/uploads/filename.jpg
 */
app.use('/uploads', express.static('uploads'));

/**
 * ====================
 * 统一响应格式中间件
 * ====================
 * 功能：统一API响应格式，便于前端处理
 * 响应格式：{ status: 0/1, msg: '消息', data: 数据 }
 */
app.use((req, res, next) => {
  // 保存原始的res.json方法，以便后续调用
  const originalJson = res.json;
  
  /**
   * 重写res.json方法
   * 对所有响应进行统一格式化处理
   */
  res.json = function(data) {
    // 如果响应数据已经是统一格式（包含status字段），直接返回
    if (data && (data.status === 0 || data.status === 1)) {
      return originalJson.call(this, data);
    }
    
    // 如果是成功响应（2xx状态码），包装成统一成功格式
    if (res.statusCode >= 200 && res.statusCode < 300) {
      return originalJson.call(this, {
        status: 0,           // 0表示成功
        msg: '操作成功',     // 成功消息
        data: data           // 实际数据
      });
    }
    
    // 其他情况（如错误响应）直接返回原始数据
    return originalJson.call(this, data);
  };
  
  next(); // 继续执行下一个中间件
});

/**
 * ====================
 * API路由注册
 * ====================
 * 功能：注册所有业务路由模块
 * 所有API路由都以/member-api为前缀，与前端Vue项目配置保持一致
 * 示例：http://localhost:3000/member-api/auth/login
 */
app.use('/member-api', authRoutes);        // 认证路由：登录、注册、退出等
app.use('/member-api', appRoutes);         // 应用通用路由：配置、基本信息等
app.use('/member-api', fundRoutes);        // 资金路由：充值、提现、余额查询等
app.use('/member-api', cardRoutes);        // 卡片路由：开卡、卡片列表、交易记录等
app.use('/member-api', dashboardRoutes);   // 仪表板路由：统计数据、图表数据等

/**
 * ====================
 * 健康检查端点
 * ====================
 * 功能：提供服务器健康状态检查，用于负载均衡器、监控系统等
 * 访问路径：GET http://localhost:3000/health
 */
app.get('/health', (req, res) => {
  res.status(200).json({
    status: 0,
    msg: '服务器运行正常',
    data: {
      service: process.env.APP_NAME,        // 应用名称
      version: process.env.APP_VERSION,     // 应用版本
      timestamp: new Date().toISOString()   // 当前时间戳
    }
  });
});

/**
 * ====================
 * 404处理中间件
 * ====================
 * 功能：处理所有未匹配的路由请求，返回统一的404错误
 * 注意：这个中间件应该放在所有路由之后
 */
app.use('*', (req, res) => {
  res.status(404).json({
    status: 1,                              // 1表示错误
    msg: `接口不存在: ${req.originalUrl}`,  // 错误消息包含请求的URL
    data: null
  });
});

/**
 * ====================
 * 错误处理中间件
 * ====================
 * 功能：统一处理应用中抛出的所有错误
 * 注意：这个中间件必须放在所有中间件和路由的最后
 */
app.use(errorHandler);

/**
 * ====================
 * 端口可用性检查函数
 * ====================
 * 功能：检查指定端口是否可用（未被占用）
 * 原理：尝试在该端口创建TCP服务器，根据结果判断端口状态
 *
 * @param {number} port - 要检查的端口号
 * @returns {Promise<boolean>} 端口是否可用
 */
function isPortAvailable(port) {
  return new Promise((resolve) => {
    // 创建TCP服务器用于端口检测
    const server = net.createServer();
    
    // 端口被占用时的错误处理
    server.once('error', (err) => {
      if (err.code === 'EADDRINUSE') {
        // 端口已被占用
        resolve(false);
      } else {
        // 其他错误也认为端口不可用
        resolve(false);
      }
    });
    
    // 端口可用时的处理
    server.once('listening', () => {
      // 立即关闭服务器，释放端口
      server.close();
      resolve(true);
    });
    
    // 尝试监听指定端口
    server.listen(port);
  });
}

/**
 * ====================
 * 查找可用端口函数
 * ====================
 * 功能：从基准端口开始，顺序查找可用的端口
 * 使用场景：开发环境中避免端口冲突
 *
 * @param {number} basePort - 基准端口号
 * @param {number} maxAttempts - 最大尝试次数，默认10次
 * @returns {Promise<number>} 可用的端口号
 * @throws {Error} 在最大尝试次数后仍未找到可用端口时抛出错误
 */
async function findAvailablePort(basePort, maxAttempts = 10) {
  let currentPort = basePort;
  let attempts = 0;
  
  // 循环尝试查找可用端口
  while (attempts < maxAttempts) {
    const available = await isPortAvailable(currentPort);
    if (available) {
      return currentPort; // 找到可用端口
    }
    console.log(`端口 ${currentPort} 被占用，尝试端口 ${currentPort + 1}`);
    currentPort++;
    attempts++;
  }
  
  // 超过最大尝试次数，抛出错误
  throw new Error(`在 ${maxAttempts} 次尝试后未能找到可用端口`);
}

/**
 * ====================
 * 服务器启动函数
 * ====================
 * 功能：执行服务器启动前的准备工作并启动Express服务器
 * 准备工作包括：数据库连接测试、端口可用性检查等
 *
 * @returns {Promise<number>} 实际使用的端口号
 */
async function startServer() {
  try {
    /**
     * 第一步：测试数据库连接
     * 确保服务器启动前数据库连接正常，避免启动后无法提供服务
     */
    const dbConnected = await testConnection();
    if (!dbConnected) {
      console.error('❌ 数据库连接失败，服务器启动中止');
      process.exit(1); // 退出程序，返回错误代码1
    }

    /**
     * 第二步：查找可用的端口
     * 避免端口冲突，特别是在开发环境中
     */
    const availablePort = await findAvailablePort(PORT);
    
    /**
     * 第三步：启动服务器监听
     * 在找到的可用端口上启动HTTP服务器
     */
    app.listen(availablePort, () => {
      // 打印启动成功信息
      console.log('='.repeat(50));
      console.log(`🚀 ${process.env.APP_NAME} 服务器启动成功`);
      console.log('='.repeat(50));
      console.log(`📊 环境: ${process.env.NODE_ENV}`);                    // 运行环境
      console.log(`🌐 地址: http://localhost:${availablePort}`);          // 服务器地址
      console.log(`🔗 健康检查: http://localhost:${availablePort}/health`); // 健康检查地址
      console.log(`🗄️  数据库: ${process.env.DB_NAME}@${process.env.DB_HOST}:${process.env.DB_PORT}`); // 数据库信息
      console.log('='.repeat(50));
    });
    
    return availablePort; // 返回实际使用的端口号
  } catch (error) {
    console.error('❌ 启动服务器失败:', error.message);
    process.exit(1); // 启动失败，退出程序
  }
}

/**
 * ====================
 * 优雅关闭处理
 * ====================
 * 功能：处理系统信号，实现优雅关闭服务器
 * 使用场景：在服务器关闭前完成清理工作，如关闭数据库连接等
 */

// SIGINT信号：通常由Ctrl+C触发
process.on('SIGINT', () => {
  console.log('\n🛑 收到关闭信号，正在优雅关闭服务器...');
  process.exit(0); // 正常退出
});

// SIGTERM信号：通常由系统关闭或容器编排系统发送
process.on('SIGTERM', () => {
  console.log('\n🛑 收到终止信号，正在优雅关闭服务器...');
  process.exit(0); // 正常退出
});

/**
 * ====================
 * 未捕获异常处理
 * ====================
 * 功能：处理应用中未捕获的异常和Promise拒绝，避免服务器崩溃
 * 生产环境最佳实践：记录错误并重启进程
 */

// 处理未捕获的同步异常
process.on('uncaughtException', (error) => {
  console.error('❌ 未捕获的异常:', error);
  process.exit(1); // 异常退出
});

// 处理未处理的Promise拒绝
process.on('unhandledRejection', (reason, promise) => {
  console.error('❌ 未处理的Promise拒绝:', reason);
  process.exit(1); // 异常退出
});

/**
 * ====================
 * 模块导出
 * ====================
 * 功能：导出app实例和启动函数，支持模块化使用
 * 使用场景：测试、作为模块被其他应用引用等
 */
module.exports = {
  app,         // Express应用实例
  startServer  // 服务器启动函数
};

/**
 * ====================
 * 自动启动逻辑
 * ====================
 * 功能：当文件被直接运行时自动启动服务器
 * 判断逻辑：require.main === module 表示当前文件是主模块
 */
if (require.main === module) {
  startServer(); // 自动启动服务器
}